-------------------------------------------------------------------------------
-- victim_cache_rtl.vhd
-------------------------------------------------------------------------------
--
-- This file is part of SKUMLI.
-- Copyright (C) 2011 Davide Giuseppe Monaco (black.ralkass@gmail.com)
--
-- SKUMLI is free software: you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation, either version 3 of the License, or
-- (at your option) any later version.
--
-- SKUMLI is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-- GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License
-- along with SKUMLI.  If not, see <http://www.gnu.org/licenses/>.
--
-------------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
-------------------------------------------------------------------------------
architecture rtl of victim_cache is

  -- 4 Blocks of 16 Bytes
  type v_cache_blocks is array (0 to 3) of std_logic_vector(0 to 127);

  -- 4 Blocks of 30 bits :: Tag (23), Index(5), Valid (1), Dirty (1)
  type v_cache_table  is array (0 to 3) of std_logic_vector(0 to 29);

  -- cache memory and table
  signal mem    : v_cache_blocks;
  signal tab    : v_cache_table;

  -- FSM related signals 
  signal state  : std_logic_vector(1 downto 0);
  signal h_bus  : std_logic;                     -- hit
  signal q_bus  : std_logic_vector(31 downto 0); -- dout

  -- internal control signals
  signal t_bus  : std_logic; -- tag comparator - H: true; L: false
  signal i_bus  : std_logic; -- index comparator - H: true; L: false
  signal v_bus  : std_logic; -- valid comparator - H: true; L: false
  signal busy   : std_logic;

  -- address support signals
  signal tag    : std_logic_vector(22 downto 0);
  signal index  : std_logic_vector( 4 downto 0);
  signal offset : std_logic_vector( 3 downto 0);

  -- memory block debug
  signal block0 : std_logic_vector(127 downto 0);
  signal block1 : std_logic_vector(127 downto 0);
  signal block2 : std_logic_vector(127 downto 0);
  signal block3 : std_logic_vector(127 downto 0);

  -- memory table debug
  signal tab0 : std_logic_vector(29 downto 0);
  signal tab1 : std_logic_vector(29 downto 0);
  signal tab2 : std_logic_vector(29 downto 0);
  signal tab3 : std_logic_vector(29 downto 0);

begin -- rtl
-------------------------------------------------------------------------------
  -- debug block assignment
  block0 <= mem(0);
  block1 <= mem(1);
  block2 <= mem(2);
  block3 <= mem(3);
  
  tab0 <= tab(0);
  tab1 <= tab(1);
  tab2 <= tab(2);
  tab3 <= tab(3);
  -- end debug block assignment

  -- address support signals assignment
  tag    <= addr(31 downto 9);
  index  <= addr( 8 downto 4);
  offset <= addr( 3 downto 0);
  -- end address support signals assignment

-------------------------------------------------------------------------------
  -- this process is used for simulation purpose 
  busy_gen: process (state)
  begin -- busy_gen 

    if state = "01" or state = "10" then
      busy <= '1', '0' after 8 ns; -- simulate component delay

    else
      busy <= '0';

    end if;

  end process busy_gen;

-------------------------------------------------------------------------------
  -- valid is HIGH if the following conditions are satisfied:
  --   1. state is read
  --   2. current block's valid bit is HIGH
  v_bus_gen: process (state, index)
    
    variable valid : std_logic;

  begin -- v_bus_gen

    if state = "01" and tab(conv_integer(index(1 downto 0)))(28) = '1' then
      valid := '1';

    else
      valid := '0';

    end if;

    v_bus <= valid;

  end process v_bus_gen;

-------------------------------------------------------------------------------
  -- index flag is HIGH if the following conditions are satisfied:
  --   1. state is read
  --   2. current block's index is equals to current index
  i_bus_gen: process (state, index)
    
    variable i : std_logic;

  begin -- i_bus_gen

    if state = "01" and index = tab(conv_integer(index(1 downto 0)))(23 to 27) then
      i := '1';

    else
      i := '0';

    end if;

    i_bus <= i;

  end process i_bus_gen;

-------------------------------------------------------------------------------
  -- tag flag is HIGH if the following conditions are satisfied:
  --   1. state is read
  --   2. current block's tag is equals to current tag
  t_bus_gen: process (state, tag, index)
    
    variable t : std_logic;

  begin -- t_bus_gen

    if state = "01" and tag = tab(conv_integer(index(1 downto 0)))(0 to 22) then
      t := '1';

    else
      t := '0';

    end if;

    t_bus <= t;

  end process t_bus_gen;

-------------------------------------------------------------------------------
  -- tab out, communicates current block tab to cache controller
  tout_gen: process (state)

    variable t : std_logic_vector(29 downto 0) := (others => '0');

  begin -- tout_gen

    if state = "01" then
      t := tab(conv_integer(index(1 downto 0)));
    end if;

    tout <= t;

  end process tout_gen;

-------------------------------------------------------------------------------
  -- hit is HIGH if the following conditions are satisfied:
  --   1. state is read
  --   2. tag corresponds with tab(i)(0 to 22) 
  --   3. index corresponds with tab(i)(23 to 27) 
  --   3. valid bit is HIGH - tab(index)(28) is high
  h_bus_gen: process (state, t_bus)

    variable hit : std_logic;

  begin -- h_bus_gen

    if state = "01" and t_bus = '1' and i_bus = '1' and v_bus = '1' then
      hit := '1';

    else
      hit := '0';

    end if;

    h_bus <= hit;

  end process h_bus_gen;

-------------------------------------------------------------------------------
  -- data out is filled with valid data if the following conditions are
  -- satisfied:
  --   1. state is read
  --   2. valid bit is HIGH
  --   3. tag flag is HIGH
  dout_gen: process (state, tag, index, offset, t_bus)

    variable init : boolean := false;
    
    variable q0 : std_logic_vector(31 downto 0);
    variable q1 : std_logic_vector(31 downto 0);
    variable q2 : std_logic_vector(31 downto 0);
    variable q3 : std_logic_vector(31 downto 0);

    -- integer index/offset support variables
    variable int_index  : integer;

  begin -- dout_gen

    if init = false then
      q0 := (others => '0');
      q1 := (others => '0');
      q2 := (others => '0');
      q3 := (others => '0');
      init := true;
    end if;

    -- write in buffer block content TAG-independently.
    if state = "01" then  
      -- calculate block index
      int_index  := conv_integer(index(1 downto 0));

      q0 := mem(int_index)( 0 to  31);
      q1 := mem(int_index)(32 to  63);
      q2 := mem(int_index)(64 to  95);
      q3 := mem(int_index)(96 to 127);

    end if;

    dout0 <= q0;
    dout1 <= q1;
    dout2 <= q2;
    dout3 <= q3;

  end process dout_gen;

-------------------------------------------------------------------------------
  -- when asked to write calculate index and offset properly, then write mem
  -- and tab
  write: process (state)
    -- initialization flag
    variable init    : boolean := false;

    -- integer index/offset support variables
    variable int_index  : integer;
    variable int_offset : integer;
    variable bit_offset : integer;
    variable end_offset : integer;

  begin -- write
-------------------------------------------------------------------------------
--  BLOCK INITIALIZATION
-------------------------------------------------------------------------------
    if init = false then

      for i in 0 to 3 loop
        mem(i) <= (others => '0');
        tab(i) <= (others => '0');
      end loop;

      init := true;

    end if;
-------------------------------------------------------------------------------
--  END BLOCK INITIALIZATION
-------------------------------------------------------------------------------

    if state = "10" then
      -- calculate block index
      int_index  := conv_integer(index(1 downto 0));

      -- calculate block offset
      int_offset := conv_integer(offset);
      bit_offset := int_offset * 8;
      end_offset := bit_offset + 31;

      -- write mem
      mem(int_index)(bit_offset to end_offset) <= din;

      -- tag
      tab(int_index)( 0 to 22) <= tag;
      tab(int_index)(23 to 27) <= index;
      tab(int_index)(28)       <= set_v; -- always valid when written
      tab(int_index)(29)       <= set_d; -- set dirty bit 

    end if;

  end process write;

-------------------------------------------------------------------------------
  main: process (cs, rw, state, busy)

    -- initialization flag
    variable init    : boolean := false;

    -- state support variable
    variable s       : std_logic_vector(1 downto 0);

  begin -- main
-------------------------------------------------------------------------------
--  STATE INITIALIZATION
-------------------------------------------------------------------------------
    if init = false then

      state <= "00";
      init := true;

    end if;
-------------------------------------------------------------------------------
--  END STATE INITIALIZATION
-------------------------------------------------------------------------------

    case state is

      -- IDLE
      when "00" =>
        if cs = '1' and rw = '0' and busy = '0' then    -- READ
          s := "01";

        elsif cs = '1' and rw = '1' and busy = '0' then -- WRITE
          s := "10";

        else                             -- IDLE
          s := "00";

        end if;

      -- READ
      when "01" =>                       -- IDLE, after work is done
        if busy = '1' then
          s := "01";

        else
          s := "00";

        end if;

      -- WRITE
      when "10" =>                       -- IDLE, after work is done
        if busy = '1' then
          s := "10";

        else
          s := "00";

        end if;

      when others =>
        s := "00";

    end case;

    -- NOTE: 1 ns commutation is reserved to:
    --       IDLE -> READ
    --       IDLE -> WRITE
    --       viceversa follows busy signal (10 ns).
    state <= s after 1 ns;

  end process main;
-------------------------------------------------------------------------------

  cmb: process (state, h_bus, q_bus)
  begin -- cmb

    case state is
      -- IDLE
      when "00" =>
        rdy   <= '1';
        hit   <= '0';

      -- READ
      when "01" =>
        rdy   <= '0';
        hit   <= h_bus;

      -- WRITE
      when "10" =>
        rdy   <= '0';
        hit   <= '0';

      when others =>
        rdy   <= '0';
        hit   <= '0';

    end case;

  end process cmb;
-------------------------------------------------------------------------------
end rtl;
